home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / tde3.zip / TAB.C < prev    next >
C/C++ Source or Header  |  1993-06-05  |  27KB  |  953 lines

  1. /*
  2.  * Most of the tab routines were gathered into one file.  There is an
  3.  * assembly routine tdeasm.c that expands tabs.  That routine is in
  4.  * assembly to keep screen updates fairly fast.
  5.  *
  6.  * The basic detab and entab algorithms were supplied by Dave Regan,
  7.  *   regan@jacobs.cs.orst.edu
  8.  *
  9.  * For more info on tabs see:
  10.  *
  11.  *     Brian W. Kernighan and P. J. Plauger, _Software Tools_, Addison-
  12.  *     Wesley Publishing Company, Reading, Mass, 1976, pp 18-27 and 35-39,
  13.  *     ISBN 0-20103669-X.
  14.  *
  15.  * The above reference gives info on fixed and variable tabs.  But when
  16.  *  it comes to non-fixed tabs, I prefer "smart" tabs.  Being lazy, I find
  17.  *  it more convenient to let the editor figure variable tabs for me.
  18.  *
  19.  *
  20.  * New editor name:  TDE, the Thomson-Davis Editor.
  21.  * Author:           Frank Davis
  22.  * Date:             June 5, 1991, version 1.0
  23.  * Date:             July 29, 1991, version 1.1
  24.  * Date:             October 5, 1991, version 1.2
  25.  * Date:             January 20, 1992, version 1.3
  26.  * Date:             February 17, 1992, version 1.4
  27.  * Date:             April 1, 1992, version 1.5
  28.  * Date:             June 5, 1992, version 2.0
  29.  * Date:             October 31, 1992, version 2.1
  30.  * Date:             April 1, 1993, version 2.2
  31.  * Date:             June 5, 1993, version 3.0
  32.  *
  33.  * This program is released into the public domain, Frank Davis.
  34.  *   You may distribute it freely.
  35.  */
  36.  
  37. #include "tdestr.h"
  38. #include "common.h"
  39. #include "tdefunc.h"
  40. #include "define.h"
  41.  
  42.  
  43. /*
  44.  * Name:    tab_key
  45.  * Purpose: To make the necessary changes after the user types the tab key.
  46.  * Date:    June 5, 1991
  47.  * Passed:  window:  pointer to current window
  48.  * Notes:   If in insert mode, then this function adds the required
  49.  *           number of spaces in the file.
  50.  *          If not in insert mode, then tab simply moves the cursor right
  51.  *           the required distance.
  52.  */
  53. int  tab_key( WINDOW *window )
  54. {
  55. int  spaces;    /* the spaces to move to the next tab stop */
  56. char *source;   /* source for block move to make room for c */
  57. char *dest;     /* destination for block move */
  58. int  pad;
  59. int  len;
  60. register int rcol;
  61. int  old_bcol;
  62. register WINDOW *win;   /* put window pointer in a register */
  63. int  rc;
  64.  
  65.    win  = window;
  66.    if (win->ll->len  ==  EOF)
  67.       return( OK );
  68.    rcol = win->rcol;
  69.    old_bcol = win->bcol;
  70.    show_ruler_char( win );
  71.    /*
  72.     * work out the number of spaces to the next tab stop
  73.     */
  74.    if (mode.smart_tab)
  75.       spaces = next_smart_tab( win );
  76.    else
  77.       spaces = mode.ltab_size - (rcol % mode.ltab_size);
  78.  
  79.    assert( spaces >= 0 );
  80.    assert( spaces < MAX_LINE_LENGTH );
  81.  
  82.    rc = OK;
  83.    if (mode.insert && rcol + spaces < g_display.line_length) {
  84.       copy_line( win->ll );
  85.       detab_linebuff( );
  86.  
  87.       /*
  88.        * work out how many characters need to be inserted
  89.        */
  90.       len = g_status.line_buff_len;
  91.       pad = rcol > len ? rcol - len : 0;
  92.       if (len + pad + spaces >= g_display.line_length) {
  93.          /*
  94.           *  line too long to add
  95.           */
  96.          error( WARNING, win->bottom_line, ed1 );
  97.          rc = ERROR;
  98.          g_status.copied = FALSE;
  99.       } else {
  100.          if (pad > 0  || spaces > 0) {
  101.             source = g_status.line_buff + rcol - pad;
  102.             dest = source + pad + spaces;
  103.  
  104.             assert( len + pad - rcol >= 0 );
  105.             assert( len + pad - rcol < MAX_LINE_LENGTH );
  106.  
  107.             memmove( dest, source, len + pad - rcol );
  108.  
  109.             /*
  110.              * if padding was required, then put in the required spaces
  111.              */
  112.  
  113.             assert( pad + spaces >= 0 );
  114.             assert( pad + spaces < MAX_LINE_LENGTH );
  115.  
  116.             memset( source, ' ', pad + spaces );
  117.             g_status.line_buff_len += pad + spaces;
  118.             entab_linebuff( );
  119.          }
  120.  
  121.          win->ll->dirty = TRUE;
  122.          win->file_info->dirty = GLOBAL;
  123.          show_changed_line( win );
  124.          rcol += spaces;
  125.          win->ccol += spaces;
  126.       }
  127.    } else if (rcol + spaces <= g_display.line_length) {
  128.       /*
  129.        * advance the cursor without changing the text underneath
  130.        */
  131.       rcol += spaces;
  132.       win->ccol += spaces;
  133.    }
  134.    check_virtual_col( win, rcol, win->ccol );
  135.    if (old_bcol != win->bcol) {
  136.       make_ruler( win );
  137.       show_ruler( win );
  138.    }
  139.    return( rc );
  140. }
  141.  
  142.  
  143. /*
  144.  * Name:    backtab
  145.  * Purpose: To make the necessary changes after the user presses the backtab.
  146.  * Date:    November 1, 1991
  147.  * Passed:  window:  pointer to current window
  148.  * Notes:   If in insert mode, then this function subs the required
  149.  *           number of spaces in the file.
  150.  *          If not in insert mode, then tab simply moves the cursor left
  151.  *           the required distance.
  152.  */
  153. int  backtab( WINDOW *window )
  154. {
  155. int  spaces;    /* the spaces to move to the next tab stop */
  156. char *source;   /* source for block move to make room for c */
  157. char *dest;     /* destination for block move */
  158. int  pad;
  159. int  len;
  160. register int rcol;
  161. int  old_bcol;
  162. register WINDOW *win;   /* put window pointer in a register */
  163.  
  164.    win  = window;
  165.    rcol = win->rcol;
  166.    if (win->ll->len == EOF || win->rcol == 0)
  167.       return( OK );
  168.    old_bcol = win->bcol;
  169.    show_ruler_char( win );
  170.  
  171.    /*
  172.     * work out the number of spaces to the previous tab stop
  173.     */
  174.    if (mode.smart_tab)
  175.       spaces = prev_smart_tab( win );
  176.    else
  177.       spaces = win->rcol % mode.ltab_size;
  178.  
  179.    if (spaces == 0)
  180.       spaces = mode.ltab_size;
  181.    copy_line( win->ll );
  182.    detab_linebuff( );
  183.    len = g_status.line_buff_len;
  184.    if (mode.insert && rcol - spaces < len) {
  185.       pad = rcol > len ? rcol - len : 0;
  186.       if (pad > 0  || spaces > 0) {
  187.          /*
  188.           * if padding was required, then put in the required spaces
  189.           */
  190.          if (pad > 0) {
  191.  
  192.             assert( rcol - pad >= 0 );
  193.             assert( pad < MAX_LINE_LENGTH );
  194.  
  195.             source = g_status.line_buff + rcol - pad;
  196.             dest = source + pad;
  197.  
  198.             assert( pad >= 0 );
  199.             assert( pad < MAX_LINE_LENGTH );
  200.  
  201.             memmove( dest, source, pad );
  202.             memset( source, ' ', pad );
  203.             g_status.line_buff_len += pad;
  204.          }
  205.          source = g_status.line_buff + rcol;
  206.          dest = source - spaces;
  207.  
  208.          assert( len + pad - rcol >= 0 );
  209.          assert( len + pad - rcol < MAX_LINE_LENGTH );
  210.  
  211.          memmove( dest, source, len + pad - rcol );
  212.          g_status.line_buff_len -= spaces;
  213.          entab_linebuff( );
  214.       }
  215.  
  216.       win->ll->dirty = TRUE;
  217.       win->file_info->dirty = GLOBAL;
  218.       show_changed_line( win );
  219.       rcol -= spaces;
  220.       win->ccol -= spaces;
  221.    } else {
  222.       /*
  223.        * move the cursor without changing the text underneath
  224.        */
  225.       rcol -= spaces;
  226.       if (rcol < 0)
  227.          rcol = 0;
  228.       win->ccol -= spaces;
  229.    }
  230.    check_virtual_col( win, rcol, win->ccol );
  231.    if (old_bcol != win->bcol) {
  232.       make_ruler( win );
  233.       show_ruler( win );
  234.    }
  235.    return( OK );
  236. }
  237.  
  238.  
  239. /*
  240.  * Name:    next_smart_tab
  241.  * Purpose: To find next smart tab
  242.  * Date:    June 5, 1992
  243.  * Passed:  window: pointer to the current window
  244.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  245.  *            current line, 2) find the first non-blank character after
  246.  *            column of the cursor.
  247.  */
  248. int  next_smart_tab( WINDOW *window )
  249. {
  250. register int spaces;    /* the spaces to move to the next tab stop */
  251. text_ptr s;             /* pointer to text */
  252. line_list_ptr ll;
  253. register WINDOW *win;   /* put window pointer in a register */
  254. int  len;
  255.  
  256.    /*
  257.     * find first previous non-blank line above the cursor.
  258.     */
  259.    win = window;
  260.    ll = win->ll->prev;
  261.    while (ll != NULL  && is_line_blank( ll->line, ll->len ))
  262.       ll = ll->prev;
  263.  
  264.    if (ll != NULL) {
  265.       s = ll->line;
  266.       /*
  267.        * if cursor is past the eol of the smart line, lets find the
  268.        *   next fixed tab.
  269.        */
  270.       if (window->rcol >= find_end( s, ll->len ))
  271.          spaces = mode.ltab_size - (window->rcol % mode.ltab_size);
  272.       else {
  273.  
  274.          len = ll->len;
  275.          s = detab_a_line( s, &len );
  276.  
  277.          spaces = 0;
  278.          s = s + window->rcol;
  279.          len -= window->rcol;
  280.  
  281.          /*
  282.           * if we are on a word, find the end of it.
  283.           */
  284.          while (*s != ' '  &&  len > 0) {
  285.             ++s;
  286.             ++spaces;
  287.             --len;
  288.          }
  289.  
  290.          /*
  291.           * now find the start of the next word.
  292.           */
  293.          if (len > 0)
  294.             while (*s == ' ' && len > 0) {
  295.                ++s;
  296.                ++spaces;
  297.                --len;
  298.             }
  299.       }
  300.    } else
  301.       spaces = mode.ltab_size - (window->rcol % mode.ltab_size);
  302.    return( spaces );
  303. }
  304.  
  305.  
  306. /*
  307.  * Name:    prev_smart_tab
  308.  * Purpose: To find previous smart tab
  309.  * Date:    June 5, 1992
  310.  * Passed:  window: pointer to the current window
  311.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  312.  *            current line, 2) find the first non-blank character before
  313.  *            column of the cursor.
  314.  *          there are several cases to consider:  1) the cursor is past the
  315.  *            the end of the smart line, 2) the smart pointer is in the
  316.  *            middle of a word, 3) there are no more words between the
  317.  *            smart pointer and the beginning of the line.
  318.  */
  319. int  prev_smart_tab( WINDOW *window )
  320. {
  321. register int spaces;    /* the spaces to move to the next tab stop */
  322. text_ptr s;             /* pointer to text */
  323. int  len;
  324. line_list_ptr ll;
  325. WINDOW *win;            /* put window pointer in a register */
  326.  
  327.    /*
  328.     * find first previous non-blank line above the cursor, if it exists.
  329.     */
  330.    win = window;
  331.    ll = win->ll->prev;
  332.    while (ll != NULL  && is_line_blank( ll->line, ll->len ))
  333.       ll = ll->prev;
  334.  
  335.    if (ll != NULL) {
  336.       s = ll->line;
  337.  
  338.       /*
  339.        * if there are no words between the cursor and column 1 of the
  340.        *   smart tab line, find previous fixed tab.
  341.        */
  342.       if (window->rcol < first_non_blank( s, ll->len ))
  343.          spaces = window->rcol % mode.ltab_size;
  344.       else {
  345.  
  346.          len = ll->len;
  347.          if (mode.inflate_tabs)
  348.             s = detab_a_line( s, &len );
  349.  
  350.          /*
  351.           * now, we need to figure the initial pointer and space.
  352.           *   if the cursor is past the eol of the smart line, then
  353.           *   set the smart pointer "s" to the end of line and "spaces" to
  354.           *   the number of characters between the cursor and the eol
  355.           *   of the smart line.  otherwise, set the smart pointer "s" to
  356.           *   the column of the cursor and "spaces" to 0.
  357.           */
  358.          if (len < window->rcol) {
  359.             s += len;
  360.             spaces = window->rcol - len;
  361.          } else {
  362.             len = window->rcol;
  363.             s += window->rcol;
  364.             spaces = 0;
  365.          }
  366.  
  367.          while (*(s-1) == ' ' && len > 0) {
  368.             --s;
  369.             ++spaces;
  370.             --len;
  371.          }
  372.  
  373.          /*
  374.           * now find the beginning of the first word at eol.
  375.           */
  376.          while (*(s-1) != ' '  &&  len > 0) {
  377.             --s;
  378.             ++spaces;
  379.             --len;
  380.          }
  381.          if (len == 0 && *s == ' ')
  382.             spaces = window->rcol % mode.ltab_size;
  383.          if (spaces > window->rcol)
  384.             spaces = window->rcol;
  385.       }
  386.    } else
  387.       spaces = window->rcol % mode.ltab_size;
  388.  
  389.    /*
  390.     * spaces cannot be negative.
  391.     */
  392.    if (spaces < 0)
  393.       spaces = 0;
  394.    return( spaces );
  395. }
  396.  
  397.  
  398. /*
  399.  * Name:    entab
  400.  * Purpose: To compress spaces to tabs
  401.  * Date:    October 31, 1992
  402.  * Passed:  s: pointer to current line
  403.  * Returns: pointer to tabout_buf
  404.  * Notes:   the text_ptr can point to either the g_status.line_buff or
  405.  *            a line in the main text buffer.
  406.  *          this function assumes that a '\n' terminates the line.
  407.  */
  408. text_ptr entab( text_ptr s, int len )
  409. {
  410. int  tab_size;
  411. int  last_col;
  412. int  space;
  413. register int col;
  414. text_ptr to;
  415.  
  416.    assert( s != NULL );
  417.    assert( len >= 0 );
  418.    assert( len < MAX_LINE_LENGTH );
  419.  
  420.    tab_size = mode.ptab_size;
  421.    to = (text_ptr)g_status.tabout_buff;
  422.    if (s == NULL)
  423.       g_status.tabout_buff_len = 0;
  424.    else {
  425.       g_status.tabout_buff_len = len;
  426.       for (last_col=col=0; ; s++, len--) {
  427.          if (len == 0) {
  428.  
  429.             /*
  430.              * when we reach the eol, compress trailing spaces to tabs.
  431.              *   the un_copy_line function is responsible for trimming
  432.              *   trailing space.
  433.              */
  434.             if (col != last_col) {
  435.                while (last_col < col) {
  436.                   space = tab_size - last_col % tab_size;
  437.                   if (space <= 1) {
  438.                      *to++ = ' ';
  439.                      last_col++;
  440.                   } else if (last_col + space <= col) {
  441.                      *to++ = '\t';
  442.                      last_col += space;
  443.                      g_status.tabout_buff_len -= (space - 1);
  444.                   } else {
  445.                      *to++ = ' ';
  446.                      last_col++;
  447.                   }
  448.                }
  449.             }
  450.  
  451.             /*
  452.              * stop entabbing when we get to EOL
  453.              */
  454.             break;
  455.          } else if (*s == ' ')
  456.             col++;
  457.          else {
  458.             if (col != last_col) {
  459.                while (last_col < col) {
  460.                   space = tab_size - last_col % tab_size;
  461.  
  462.                   /*
  463.                    * when space == 1, forget about emmitting a tab
  464.                    *   for 1 space.
  465.                    */
  466.                   if (space <= 1) {
  467.                      *to++ = ' ';
  468.                      last_col++;
  469.                   } else if (last_col + space <= col) {
  470.                      *to++ = '\t';
  471.                      last_col += space;
  472.                      g_status.tabout_buff_len -= (space - 1);
  473.                   } else {
  474.                      *to++ = ' ';
  475.                      last_col++;
  476.                   }
  477.                }
  478.             }
  479.  
  480.             /*
  481.              * if *s == tab, then adjust the col pointer
  482.              */
  483.             if (*s == '\t')
  484.                col = col + tab_size - (col % tab_size);
  485.             else
  486.                ++col;
  487.             last_col = col;
  488.             *to++ = *s;
  489.  
  490.             /*
  491.              * when we see a quote character, stop entabbing.
  492.              */
  493.             if (*s == '\"' || *s == '\'') {
  494.                while (len > 0) {
  495.                  *to++ = *++s;
  496.                  --len;
  497.                }
  498.                break;
  499.             }
  500.          }
  501.       }
  502.    }
  503.    return( (text_ptr)g_status.tabout_buff );
  504. }
  505.  
  506.  
  507. /*
  508.  * Name:    detab_linebuff
  509.  * Purpose: To expand tabs in line buffer so we can handle editing functions
  510.  * Date:    October 31, 1992
  511.  * Notes:   before we do any editing function that modifies text, let's
  512.  *            expand any tabs to space.
  513.  *          if inflate_tabs is FALSE, then nothing is done.
  514.  */
  515. void detab_linebuff( void )
  516. {
  517. int  show_eol;
  518. int  len;
  519.  
  520.    if (mode.inflate_tabs  &&  g_status.copied) {
  521.       len = g_status.line_buff_len;
  522.       show_eol = mode.show_eol;
  523.       mode.show_eol = FALSE;
  524.       tabout( (text_ptr)g_status.line_buff, &len );
  525.  
  526.       assert( len >= 0 );
  527.       assert( len < MAX_LINE_LENGTH );
  528.  
  529.       memmove( g_status.line_buff, g_status.tabout_buff, len );
  530.       g_status.line_buff_len = len;
  531.       mode.show_eol = show_eol;
  532.    }
  533. }
  534.  
  535.  
  536. /*
  537.  * Name:    entab_linebuff
  538.  * Purpose: To compress space in line buffer
  539.  * Date:    October 31, 1992
  540.  * Notes:   compress spaces back to tabs in the line buffer, if
  541.  *             inflate_tabs == TRUE
  542.  */
  543. void entab_linebuff( void )
  544. {
  545.    if (mode.inflate_tabs  &&  g_status.copied) {
  546.       entab( (text_ptr)g_status.line_buff, g_status.line_buff_len );
  547.  
  548.       assert( g_status.tabout_buff_len >= 0 );
  549.       assert( g_status.tabout_buff_len < MAX_LINE_LENGTH );
  550.  
  551.       memmove( g_status.line_buff, g_status.tabout_buff,
  552.                        g_status.tabout_buff_len );
  553.       g_status.line_buff_len = g_status.tabout_buff_len;
  554.    }
  555. }
  556.  
  557.  
  558. /*
  559.  * Name:    detab_a_line
  560.  * Purpose: To inflate tabs in any line in the file
  561.  * Date:    October 31, 1992
  562.  * Returns: pointer to text or tabout_buff
  563.  * Notes:   expand an arbitrary line in the file.
  564.  */
  565. text_ptr detab_a_line( text_ptr s, int *len )
  566. {
  567. int  show_eol;
  568.  
  569.    if (mode.inflate_tabs) {
  570.  
  571.       assert( *len >= 0 );
  572.       assert( *len < MAX_LINE_LENGTH );
  573.       assert( s != NULL );
  574.  
  575.       show_eol = mode.show_eol;
  576.       mode.show_eol = FALSE;
  577.       s = tabout( s, len );
  578.       mode.show_eol = show_eol;
  579.    }
  580.    return( s );
  581. }
  582.  
  583.  
  584. /*
  585.  * Name:    detab_adjust_rcol
  586.  * Purpose: given rcol in a line, find virtual column
  587.  * Date:    October 31, 1992
  588.  * Passed:  s:  string pointer
  589.  * Notes:   without expanding tabs, calculate the display column according
  590.  *            to current tab stop.
  591.  */
  592. int  detab_adjust_rcol( text_ptr s, int rcol )
  593. {
  594. register int col;
  595.  
  596.    assert( rcol >= 0 );
  597.    assert( rcol < MAX_LINE_LENGTH );
  598.    assert( s != NULL );
  599.    assert( mode.ptab_size != 0 );
  600.  
  601.    for (col=0; rcol >= 0; rcol--,s++) {
  602.       if (*s == '\t')
  603.          col += (mode.ptab_size - (col % mode.ptab_size));
  604.       else if (rcol > 0)
  605.          col++;
  606.    }
  607.  
  608.    assert( col >= 0 );
  609.    assert( col < MAX_LINE_LENGTH );
  610.  
  611.    return( col );
  612. }
  613.  
  614.  
  615. /*
  616.  * Name:    entab_adjust_rcol
  617.  * Purpose: given virtual rcol in a line, find real column
  618.  * Date:    October 31, 1992
  619.  * Passed:  s:     string pointer
  620.  *          len:   length of string
  621.  *          rcol:  virtual real column
  622.  * Notes:   without expanding tabs, calculate which col the real cursor
  623.  *            referencing.
  624.  */
  625. int  entab_adjust_rcol( text_ptr s, int len, int rcol )
  626. {
  627. register int col;
  628. register int last_col;
  629.  
  630.    assert( len >= 0 );
  631.    assert( len < MAX_LINE_LENGTH );
  632.    assert( rcol >= 0 );
  633.    assert( rcol < MAX_LINE_LENGTH );
  634.    assert( mode.ptab_size != 0 );
  635.  
  636.    if (s != NULL) {
  637.       for (last_col=col=0; col < rcol  &&  s != NULL  &&  len > 0; s++, len--) {
  638.          if (*s != '\t')
  639.             ++col;
  640.          else
  641.             col += (mode.ptab_size - (col % mode.ptab_size));
  642.          if (col > rcol)
  643.             break;
  644.          ++last_col;
  645.       }
  646.    } else
  647.       last_col = rcol;
  648.  
  649.    assert( last_col >= 0 );
  650.    assert( last_col < MAX_LINE_LENGTH );
  651.  
  652.    return( last_col );
  653. }
  654.  
  655.  
  656. /*
  657.  * Name:    block_expand_tabs
  658.  * Purpose: Expand tabs in a marked block.
  659.  * Date:    June 5, 1991
  660.  * Passed:  window:  pointer to current window
  661.  * Notes:   Tabs are expanded using the current tab interval.
  662.  *          Lines are checked to make sure they are not too long.
  663.  */
  664. int  block_expand_tabs( WINDOW *window )
  665. {
  666. int  prompt_line;
  667. int  len;
  668. int  tab;
  669. int  tab_size;
  670. int  dirty;
  671. register int spaces;
  672. line_list_ptr p;                /* pointer to block line */
  673. file_infos *file;
  674. WINDOW *sw, s_w;
  675. long er;
  676. int  i;
  677. int  rc;
  678. char *b;
  679.  
  680.    /*
  681.     * make sure block is marked OK and that this is a LINE block
  682.     */
  683.    prompt_line = window->bottom_line;
  684.  
  685.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  686.       return( ERROR );
  687.    check_block( );
  688.    rc = OK;
  689.    if (g_status.marked == TRUE) {
  690.  
  691.       file  = g_status.marked_file;
  692.       if (file->block_type != LINE) {
  693.          /*
  694.           * can only expand tabs in line blocks
  695.           */
  696.          error( WARNING, prompt_line, block20 );
  697.          return( ERROR );
  698.       }
  699.  
  700.       /*
  701.        * initialize everything
  702.        */
  703.       dirty = FALSE;
  704.       tab_size = mode.ptab_size;
  705.       sw = g_status.window_list;
  706.       for (; ptoul( sw->file_info ) != ptoul( file );)
  707.          sw = sw->next;
  708.       dup_window_info( &s_w, sw );
  709.       p  = file->block_start;
  710.       er = file->block_er;
  711.       s_w.rline = file->block_br;
  712.       s_w.visible = FALSE;
  713.       for (; s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  714.  
  715.          /*
  716.           * use the line buffer to expand LINE blocks.
  717.           */
  718.          tab = FALSE;
  719.          g_status.copied = FALSE;
  720.  
  721.          copy_line( p );
  722.          len = g_status.line_buff_len;
  723.          for (b=g_status.line_buff, i=1; len > 0  &&  rc == OK; b++, len--) {
  724.  
  725.             /*
  726.              * each line in the LINE block is copied to the g_status.line_buff.
  727.              *  look at the text in the buffer and expand tabs.
  728.              */
  729.             if (*b == '\t') {
  730.                tab = TRUE;
  731.                spaces = i % tab_size;
  732.                if (spaces)
  733.                   spaces = tab_size - spaces;
  734.                if (spaces) {
  735.  
  736.                   assert( len >= 0 );
  737.                   assert( len < MAX_LINE_LENGTH );
  738.  
  739.                   memmove( b + spaces, b, len );
  740.                }
  741.  
  742.                assert( spaces + 1 >= 0 );
  743.                assert( spaces + 1 < MAX_LINE_LENGTH );
  744.  
  745.                memset( b, ' ', spaces+1 );
  746.                i += spaces + 1;
  747.                b += spaces;
  748.                g_status.line_buff_len += spaces;
  749.             } else
  750.                i++;
  751.          }
  752.  
  753.          /*
  754.           * if any tabs were found, write g_status.line_buff to file.
  755.           */
  756.          if (tab) {
  757.             rc = un_copy_line( p, &s_w, TRUE );
  758.             dirty = TRUE;
  759.          }
  760.          p = p->next;
  761.       }
  762.  
  763.       /*
  764.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  765.        * not necessarily be on the last line of the block.
  766.        */
  767.       g_status.copied = FALSE;
  768.       if (dirty)
  769.          file->dirty = GLOBAL;
  770.    }
  771.    return( rc );
  772. }
  773.  
  774.  
  775. /*
  776.  * Name:    block_compress_tabs
  777.  * Purpose: Expand tabs in a marked block.
  778.  * Date:    October 31, 1992
  779.  * Passed:  window:  pointer to current window
  780.  * Notes:   Tabs are compress using the current tab setting.
  781.  */
  782. int  block_compress_tabs( WINDOW *window )
  783. {
  784. register int col;
  785. register int spaces;
  786. int  len;
  787. int  rc;
  788. int  prompt_line;
  789. int  last_col;
  790. int  tab;
  791. int  tab_size;
  792. int  dirty;
  793. line_list_ptr p;                /* pointer to block line */
  794. text_ptr from;                  /* line in main text buff being compressed */
  795. file_infos *file;
  796. WINDOW *sw, s_w;
  797. long er;
  798. char *to;
  799. int  indent_only;
  800.  
  801.    /*
  802.     * make sure block is marked OK and that this is a LINE block
  803.     */
  804.    prompt_line = window->bottom_line;
  805.    entab_linebuff( );
  806.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  807.       return( ERROR );
  808.    check_block( );
  809.    rc = OK;
  810.    if (g_status.marked == TRUE) {
  811.  
  812.       file  = g_status.marked_file;
  813.       if (file->block_type != LINE) {
  814.          /*
  815.           * can only compress tabs in line blocks
  816.           */
  817.          error( WARNING, prompt_line, block26 );
  818.          return( ERROR );
  819.       }
  820.  
  821.       indent_only = g_status.command == BlockIndentTabs ? TRUE : FALSE;
  822.  
  823.       /*
  824.        * set the command to word wrap so the un_copy_line function will
  825.        * not display the lines while expanding.
  826.        */
  827.       g_status.command = WordWrap;
  828.  
  829.       /*
  830.        * initialize everything
  831.        */
  832.       dirty = FALSE;
  833.       tab_size = mode.ptab_size;
  834.       sw = g_status.window_list;
  835.       for (; ptoul( sw->file_info ) != ptoul( file );)
  836.          sw = sw->next;
  837.       dup_window_info( &s_w, sw );
  838.       s_w.visible = FALSE;
  839.       s_w.ll  =  p  = file->block_start;
  840.       er = file->block_er;
  841.       s_w.rline = file->block_br;
  842.       for (; rc == OK  &&  s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  843.  
  844.          /*
  845.           * use the line buffer to compress LINE blocks.
  846.           */
  847.          tab = FALSE;
  848.  
  849.          from = p->line;
  850.          to   = g_status.line_buff;
  851.          g_status.line_buff_len = len  = p->len;
  852.  
  853.          for (last_col=col=0; ; from++, len--) {
  854.             if (len == 0) {
  855.  
  856.                /*
  857.                 * when we reach the eol, compress trailing spaces to tabs.
  858.                 *   the un_copy_line function is responsible for trimming
  859.                 *   trailing space.
  860.                 */
  861.                if (col != last_col) {
  862.                   while (last_col < col) {
  863.                      spaces = tab_size - last_col % tab_size;
  864.                      if (spaces <= 1) {
  865.                         *to++ = ' ';
  866.                         last_col++;
  867.                      } else if (last_col + spaces <= col) {
  868.                         *to++ = '\t';
  869.                         last_col += spaces;
  870.                         g_status.line_buff_len -= (spaces - 1);
  871.                         tab = TRUE;
  872.                      } else {
  873.                         *to++ = ' ';
  874.                         last_col++;
  875.                      }
  876.                   }
  877.                }
  878.  
  879.                /*
  880.                 * stop entabbing when we get to EOL
  881.                 */
  882.                break;
  883.             } else if (*from == ' ')
  884.                col++;
  885.             else {
  886.                if (col != last_col) {
  887.                   while (last_col < col) {
  888.                      spaces = tab_size - last_col % tab_size;
  889.  
  890.                   /*
  891.                    * when space == 1, forget about emmitting a tab
  892.                    *   for 1 space.
  893.                    */
  894.                      if (spaces <= 1) {
  895.                         *to++ = ' ';
  896.                         last_col++;
  897.                      } else if (last_col + spaces <= col) {
  898.                         *to++ = '\t';
  899.                         last_col += spaces;
  900.                         g_status.line_buff_len -= (spaces - 1);
  901.                         tab = TRUE;
  902.                      } else {
  903.                         *to++ = ' ';
  904.                         last_col++;
  905.                      }
  906.                   }
  907.                }
  908.  
  909.                /*
  910.                 * if *from == tab, then adjust the col pointer
  911.                 */
  912.                if (*from == '\t')
  913.                   col = col + tab_size - (col % tab_size);
  914.                else
  915.                   ++col;
  916.                last_col = col;
  917.                *to++ = *from;
  918.  
  919.                /*
  920.                 * when we see a quote character, stop entabbing.
  921.                 */
  922.                if (*from == '\"' || *from == '\''  || indent_only) {
  923.                   while (len > 0) {
  924.                      *to++ = *++from;
  925.                      --len;
  926.                   }
  927.                   break;
  928.                }
  929.             }
  930.          }
  931.  
  932.          /*
  933.           * if any tabs were found, write g_status.line_buff to file.
  934.           */
  935.          if (tab) {
  936.             g_status.copied = TRUE;
  937.             rc = un_copy_line( p, &s_w, TRUE );
  938.             dirty = TRUE;
  939.          }
  940.          p = p->next;
  941.       }
  942.  
  943.       /*
  944.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  945.        * not necessarily be on the last line of the block.
  946.        */
  947.       g_status.copied = FALSE;
  948.       if (dirty)
  949.          file->dirty = GLOBAL;
  950.    }
  951.    return( rc );
  952. }
  953.